{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "feature_crosses.ipynb",
      "version": "0.3.2",
      "views": {},
      "default_view": {},
      "provenance": [],
      "collapsed_sections": [
        "ZTDHHM61NPTw",
        "0i7vGo9PTaZl",
        "copyright-notice"
      ]
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "copyright-notice",
        "colab_type": "text"
      },
      "source": [
        "#### Copyright 2017 Google LLC."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "copyright-notice2",
        "colab_type": "code",
        "cellView": "both",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "outputs": [],
      "execution_count": 0
    },
    {
      "metadata": {
        "id": "g4T-_IsVbweU",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " # Combinaciones de atributos"
      ]
    },
    {
      "metadata": {
        "id": "F7dke6skIK-k",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " **Objetivos de aprendizaje:**\n",
        "  * mejorar un modelo de regresi\u00f3n lineal con la incorporaci\u00f3n de atributos sint\u00e9ticos adicionales (esto es una continuaci\u00f3n del ejercicio anterior)\n",
        "  * usar una funci\u00f3n de entrada para convertir los objetos `DataFrame` de Pandas en `Tensors` e invocar la funci\u00f3n de entrada en las operaciones `fit()` y `predict()`\n",
        "  * usar el algoritmo de optimizaci\u00f3n de FTRL para el entrenamiento del modelo\n",
        "  * crear atributos sint\u00e9ticos nuevos mediante codificaci\u00f3n de un solo 1, discretizaci\u00f3n y combinaciones de atributos"
      ]
    },
    {
      "metadata": {
        "id": "NS_fcQRd8B97",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Preparaci\u00f3n"
      ]
    },
    {
      "metadata": {
        "id": "4IdzD8IdIK-l",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Primero, como lo hicimos en ejercicios anteriores, definamos la entrada y creemos el c\u00f3digo para cargar los datos."
      ]
    },
    {
      "metadata": {
        "id": "CsfdiLiDIK-n",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "from __future__ import print_function\n",
        "\n",
        "import math\n",
        "\n",
        "from IPython import display\n",
        "from matplotlib import cm\n",
        "from matplotlib import gridspec\n",
        "from matplotlib import pyplot as plt\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "from sklearn import metrics\n",
        "import tensorflow as tf\n",
        "from tensorflow.python.data import Dataset\n",
        "\n",
        "tf.logging.set_verbosity(tf.logging.ERROR)\n",
        "pd.options.display.max_rows = 10\n",
        "pd.options.display.float_format = '{:.1f}'.format\n",
        "\n",
        "california_housing_dataframe = pd.read_csv(\"https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv\", sep=\",\")\n",
        "\n",
        "california_housing_dataframe = california_housing_dataframe.reindex(\n",
        "    np.random.permutation(california_housing_dataframe.index))"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "10rhoflKIK-s",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def preprocess_features(california_housing_dataframe):\n",
        "  \"\"\"Prepares input features from California housing data set.\n",
        "\n",
        "  Args:\n",
        "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
        "      from the California housing data set.\n",
        "  Returns:\n",
        "    A DataFrame that contains the features to be used for the model, including\n",
        "    synthetic features.\n",
        "  \"\"\"\n",
        "  selected_features = california_housing_dataframe[\n",
        "    [\"latitude\",\n",
        "     \"longitude\",\n",
        "     \"housing_median_age\",\n",
        "     \"total_rooms\",\n",
        "     \"total_bedrooms\",\n",
        "     \"population\",\n",
        "     \"households\",\n",
        "     \"median_income\"]]\n",
        "  processed_features = selected_features.copy()\n",
        "  # Create a synthetic feature.\n",
        "  processed_features[\"rooms_per_person\"] = (\n",
        "    california_housing_dataframe[\"total_rooms\"] /\n",
        "    california_housing_dataframe[\"population\"])\n",
        "  return processed_features\n",
        "\n",
        "def preprocess_targets(california_housing_dataframe):\n",
        "  \"\"\"Prepares target features (i.e., labels) from California housing data set.\n",
        "\n",
        "  Args:\n",
        "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
        "      from the California housing data set.\n",
        "  Returns:\n",
        "    A DataFrame that contains the target feature.\n",
        "  \"\"\"\n",
        "  output_targets = pd.DataFrame()\n",
        "  # Scale the target to be in units of thousands of dollars.\n",
        "  output_targets[\"median_house_value\"] = (\n",
        "    california_housing_dataframe[\"median_house_value\"] / 1000.0)\n",
        "  return output_targets"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "ufplEkjN8KUp",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# Choose the first 12000 (out of 17000) examples for training.\n",
        "training_examples = preprocess_features(california_housing_dataframe.head(12000))\n",
        "training_targets = preprocess_targets(california_housing_dataframe.head(12000))\n",
        "\n",
        "# Choose the last 5000 (out of 17000) examples for validation.\n",
        "validation_examples = preprocess_features(california_housing_dataframe.tail(5000))\n",
        "validation_targets = preprocess_targets(california_housing_dataframe.tail(5000))\n",
        "\n",
        "# Double-check that we've done the right thing.\n",
        "print(\"Training examples summary:\")\n",
        "display.display(training_examples.describe())\n",
        "print(\"Validation examples summary:\")\n",
        "display.display(validation_examples.describe())\n",
        "\n",
        "print(\"Training targets summary:\")\n",
        "display.display(training_targets.describe())\n",
        "print(\"Validation targets summary:\")\n",
        "display.display(validation_targets.describe())"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "oJlrB4rJ_2Ma",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def construct_feature_columns(input_features):\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Args:\n",
        "    input_features: The names of the numerical input features to use.\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\"\n",
        "  return set([tf.feature_column.numeric_column(my_feature)\n",
        "              for my_feature in input_features])"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "NBxoAfp2AcB6",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def my_input_fn(features, targets, batch_size=1, shuffle=True, num_epochs=None):\n",
        "    \"\"\"Trains a linear regression model.\n",
        "  \n",
        "    Args:\n",
        "      features: pandas DataFrame of features\n",
        "      targets: pandas DataFrame of targets\n",
        "      batch_size: Size of batches to be passed to the model\n",
        "      shuffle: True or False. Whether to shuffle the data.\n",
        "      num_epochs: Number of epochs for which data should be repeated. None = repeat indefinitely\n",
        "    Returns:\n",
        "      Tuple of (features, labels) for next data batch\n",
        "    \"\"\"\n",
        "    \n",
        "    # Convert pandas data into a dict of np arrays.\n",
        "    features = {key:np.array(value) for key,value in dict(features).items()}                                           \n",
        " \n",
        "    # Construct a dataset, and configure batching/repeating.\n",
        "    ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit\n",
        "    ds = ds.batch(batch_size).repeat(num_epochs)\n",
        "    \n",
        "    # Shuffle the data, if specified.\n",
        "    if shuffle:\n",
        "      ds = ds.shuffle(10000)\n",
        "    \n",
        "    # Return the next batch of data.\n",
        "    features, labels = ds.make_one_shot_iterator().get_next()\n",
        "    return features, labels"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "hweDyy31LBsV",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Algoritmo de optimizaci\u00f3n FTRL\n",
        "\n",
        "Los modelos lineales de dimensiones altas se benefician del uso de una variante de optimizaci\u00f3n basada en gradientes, que se denomina FTRL. Este algoritmo tiene la ventaja de ajustar la tasa de aprendizaje de distintas maneras para diferentes coeficientes, lo cual puede resultar \u00fatil si algunos atributos raramente toman valores distintos de cero (tambi\u00e9n es adecuado cuando se utiliza regularizaci\u00f3n L1). Podemos aplicar FTRL a trav\u00e9s de la funci\u00f3n [FtrlOptimizer](https://www.tensorflow.org/api_docs/python/tf/train/FtrlOptimizer)."
      ]
    },
    {
      "metadata": {
        "id": "S0SBf1X1IK_O",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def train_model(\n",
        "    learning_rate,\n",
        "    steps,\n",
        "    batch_size,\n",
        "    feature_columns,\n",
        "    training_examples,\n",
        "    training_targets,\n",
        "    validation_examples,\n",
        "    validation_targets):\n",
        "  \"\"\"Trains a linear regression model.\n",
        "  \n",
        "  In addition to training, this function also prints training progress information,\n",
        "  as well as a plot of the training and validation loss over time.\n",
        "  \n",
        "  Args:\n",
        "    learning_rate: A `float`, the learning rate.\n",
        "    steps: A non-zero `int`, the total number of training steps. A training step\n",
        "      consists of a forward and backward pass using a single batch.\n",
        "    feature_columns: A `set` specifying the input feature columns to use.\n",
        "    training_examples: A `DataFrame` containing one or more columns from\n",
        "      `california_housing_dataframe` to use as input features for training.\n",
        "    training_targets: A `DataFrame` containing exactly one column from\n",
        "      `california_housing_dataframe` to use as target for training.\n",
        "    validation_examples: A `DataFrame` containing one or more columns from\n",
        "      `california_housing_dataframe` to use as input features for validation.\n",
        "    validation_targets: A `DataFrame` containing exactly one column from\n",
        "      `california_housing_dataframe` to use as target for validation.\n",
        "      \n",
        "  Returns:\n",
        "    A `LinearRegressor` object trained on the training data.\n",
        "  \"\"\"\n",
        "\n",
        "  periods = 10\n",
        "  steps_per_period = steps / periods\n",
        "\n",
        "  # Create a linear regressor object.\n",
        "  my_optimizer = tf.train.FtrlOptimizer(learning_rate=learning_rate)\n",
        "  my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)\n",
        "  linear_regressor = tf.estimator.LinearRegressor(\n",
        "      feature_columns=feature_columns,\n",
        "      optimizer=my_optimizer\n",
        "  )\n",
        "  \n",
        "  training_input_fn = lambda: my_input_fn(training_examples, \n",
        "                                          training_targets[\"median_house_value\"], \n",
        "                                          batch_size=batch_size)\n",
        "  predict_training_input_fn = lambda: my_input_fn(training_examples, \n",
        "                                                  training_targets[\"median_house_value\"], \n",
        "                                                  num_epochs=1, \n",
        "                                                  shuffle=False)\n",
        "  predict_validation_input_fn = lambda: my_input_fn(validation_examples, \n",
        "                                                    validation_targets[\"median_house_value\"], \n",
        "                                                    num_epochs=1, \n",
        "                                                    shuffle=False)\n",
        "\n",
        "  # Train the model, but do so inside a loop so that we can periodically assess\n",
        "  # loss metrics.\n",
        "  print(\"Training model...\")\n",
        "  print(\"RMSE (on training data):\")\n",
        "  training_rmse = []\n",
        "  validation_rmse = []\n",
        "  for period in range (0, periods):\n",
        "    # Train the model, starting from the prior state.\n",
        "    linear_regressor.train(\n",
        "        input_fn=training_input_fn,\n",
        "        steps=steps_per_period\n",
        "    )\n",
        "    # Take a break and compute predictions.\n",
        "    training_predictions = linear_regressor.predict(input_fn=predict_training_input_fn)\n",
        "    training_predictions = np.array([item['predictions'][0] for item in training_predictions])\n",
        "    validation_predictions = linear_regressor.predict(input_fn=predict_validation_input_fn)\n",
        "    validation_predictions = np.array([item['predictions'][0] for item in validation_predictions])\n",
        "    \n",
        "    # Compute training and validation loss.\n",
        "    training_root_mean_squared_error = math.sqrt(\n",
        "        metrics.mean_squared_error(training_predictions, training_targets))\n",
        "    validation_root_mean_squared_error = math.sqrt(\n",
        "        metrics.mean_squared_error(validation_predictions, validation_targets))\n",
        "    # Occasionally print the current loss.\n",
        "    print(\"  period %02d : %0.2f\" % (period, training_root_mean_squared_error))\n",
        "    # Add the loss metrics from this period to our list.\n",
        "    training_rmse.append(training_root_mean_squared_error)\n",
        "    validation_rmse.append(validation_root_mean_squared_error)\n",
        "  print(\"Model training finished.\")\n",
        "\n",
        "  \n",
        "  # Output a graph of loss metrics over periods.\n",
        "  plt.ylabel(\"RMSE\")\n",
        "  plt.xlabel(\"Periods\")\n",
        "  plt.title(\"Root Mean Squared Error vs. Periods\")\n",
        "  plt.tight_layout()\n",
        "  plt.plot(training_rmse, label=\"training\")\n",
        "  plt.plot(validation_rmse, label=\"validation\")\n",
        "  plt.legend()\n",
        "\n",
        "  return linear_regressor"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "1Cdr02tLIK_Q",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(training_examples),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "i4lGvqajDWlw",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Codificaci\u00f3n de un solo 1 para atributos discretos\n",
        "\n",
        "Los atributos discretos (es decir, strings, enumeraciones, enteros) generalmente se convierten en familias de atributos binarios antes de entrenar un modelo de regresi\u00f3n log\u00edstica.\n",
        "\n",
        "Por ejemplo, imagina que creamos un atributo sint\u00e9tico que puede tomar cualquiera de los valores `0`, `1` o `2`, y que tenemos algunos puntos de entrenamiento:\n",
        "\n",
        "| # | feature_value |\n",
        "|---|---------------|\n",
        "| 0 |             2 |\n",
        "| 1 |             0 |\n",
        "| 2 |             1 |\n",
        "\n",
        "Para cada valor categ\u00f3rico posible, generamos un nuevo atributo **binario** de **valores reales** que puede tomar uno de solo dos valores posibles: 1.0 si el ejemplo tiene ese valor y 0.0 si no lo tiene. En el ejemplo anterior, el atributo categ\u00f3rico se convertir\u00eda en tres atributos y los puntos de entrenamiento tendr\u00edan el siguiente aspecto:\n",
        "\n",
        "| # | feature_value_0 | feature_value_1 | feature_value_2 |\n",
        "|---|-----------------|-----------------|-----------------|\n",
        "| 0 |             0.0 |             0.0 |             1.0 |\n",
        "| 1 |             1.0 |             0.0 |             0.0 |\n",
        "| 2 |             0.0 |             1.0 |             0.0 |"
      ]
    },
    {
      "metadata": {
        "id": "KnssXowblKm7",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Atributos agrupados (discretizados)\n",
        "\n",
        "El agrupamiento tambi\u00e9n se conoce como discretizaci\u00f3n.\n",
        "\n",
        "Podemos agrupar `population` en los siguientes 3 agrupamientos (a modo de ejemplo):\n",
        "- `bucket_0` (`< 5000`): Corresponde a las manzanas menos pobladas.\n",
        "- `bucket_1` (`5000 - 25000`): Corresponde a las manzanas con un nivel de poblaci\u00f3n medio.\n",
        "- `bucket_2` (`> 25000`): Corresponde a las manzanas altamente pobladas.\n",
        "\n",
        "Dadas las definiciones de agrupamiento anteriores, el siguiente vector `population`:\n",
        "\n",
        "    [[10001], [42004], [2500], [18000]]\n",
        "\n",
        "se convierte en el siguiente vector de atributos agrupados:\n",
        "\n",
        "    [[1], [2], [0], [1]]\n",
        "\n",
        "Los valores de los atributos ahora son los \u00edndices de agrupamiento. Ten en cuenta que estos \u00edndices se consideran atributos discretos. Por lo general, estos se convertir\u00e1n posteriormente en representaciones de un solo 1 como se mostr\u00f3 m\u00e1s arriba, pero esto se realizar\u00e1 de manera transparente.\n",
        "\n",
        "Para definir columnas de atributos para los atributos agrupados, en lugar de usar `numeric_column`, podemos usar [`bucketized_column`](https://www.tensorflow.org/api_docs/python/tf/feature_column/bucketized_column), que toma una columna num\u00e9rica como entrada y la transforma en un atributo  agrupado a trav\u00e9s de los l\u00edmites de agrupamiento que se especifican en el argumento `boundaries`. El siguiente c\u00f3digo define columnas de atributos agrupados para `households` y `longitude`. La funci\u00f3n `get_quantile_based_boundaries` calcula los l\u00edmites en funci\u00f3n de los cuantiles, de manera que cada agrupamiento contiene el mismo n\u00famero de elementos."
      ]
    },
    {
      "metadata": {
        "id": "cc9qZrtRy-ED",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def get_quantile_based_boundaries(feature_values, num_buckets):\n",
        "  boundaries = np.arange(1.0, num_buckets) / num_buckets\n",
        "  quantiles = feature_values.quantile(boundaries)\n",
        "  return [quantiles[q] for q in quantiles.keys()]\n",
        "\n",
        "# Divide households into 7 buckets.\n",
        "households = tf.feature_column.numeric_column(\"households\")\n",
        "bucketized_households = tf.feature_column.bucketized_column(\n",
        "  households, boundaries=get_quantile_based_boundaries(\n",
        "    california_housing_dataframe[\"households\"], 7))\n",
        "\n",
        "# Divide longitude into 10 buckets.\n",
        "longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "  longitude, boundaries=get_quantile_based_boundaries(\n",
        "    california_housing_dataframe[\"longitude\"], 10))"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "U-pQDAa0MeN3",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Tarea\u00a01: Entrena el modelo con columnas de atributos agrupados\n",
        "**Agrupa todos los atributos con valores reales de nuestro ejemplo, entrena el modelo y observa si mejoran los resultados.**\n",
        "\n",
        "En el bloque de c\u00f3digo anterior, se transformaron dos columnas con valores reales (concretamente `households` y `longitude`) en columnas de atributos agrupados. Tu tarea es agrupar el resto de las columnas y, luego, ejecutar el c\u00f3digo para entrenar el modelo. Hay distintas heur\u00edsticas para buscar el rango de los agrupamientos. En este ejercicio se usa una t\u00e9cnica basada en cuantiles, que elige los l\u00edmites de los agrupamientos de manera tal que cada agrupamiento tenga el mismo n\u00famero de ejemplos."
      ]
    },
    {
      "metadata": {
        "id": "YFXV9lyMLedy",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "\n",
        "  #\n",
        "  # YOUR CODE HERE: bucketize the following columns, following the example above:\n",
        "  #\n",
        "  bucketized_latitude = \n",
        "  bucketized_housing_median_age = \n",
        "  bucketized_median_income =\n",
        "  bucketized_rooms_per_person =\n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person])\n",
        "  \n",
        "  return feature_columns\n"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "0FfUytOTNJhL",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "ZTDHHM61NPTw",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ### Soluci\u00f3n\n",
        "\n",
        "Haz clic m\u00e1s abajo para conocer la soluci\u00f3n."
      ]
    },
    {
      "metadata": {
        "id": "JQHnUhL_NRwA",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Posiblemente te preguntes c\u00f3mo determinar cu\u00e1ntos agrupamientos debes usar. Eso, desde luego, depende de los datos. Aqu\u00ed, simplemente seleccionamos valores arbitrarios como para obtener un modelo que no fuera demasiado grande."
      ]
    },
    {
      "metadata": {
        "id": "Ro5civQ3Ngh_",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person])\n",
        "  \n",
        "  return feature_columns"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "RNgfYk6OO8Sy",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "AFJ1qoZPlQcs",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Combinaciones de funciones\n",
        "\n",
        "Combinar dos (o m\u00e1s) atributos es una forma inteligente de aprender relaciones no lineales a trav\u00e9s de un modelo lineal. En nuestro problema, si simplemente usamos el atributo `latitude` para el aprendizaje, el modelo podr\u00eda aprender que las manzanas de una latitud en particular (o dentro de un rango espec\u00edfico de latitudes, dado que la agrupamos) tienen m\u00e1s probabilidades de ser m\u00e1s costosas que otras. Algo similar ocurre para el atributo `longitude`. Sin embargo, si combinamos `longitude` con `latitude`, el atributo combinado representa una manzana bien definida. Si el modelo aprende que determinadas manzanas (dentro de un rango de latitudes y longitudes) tienen m\u00e1s probabilidades de ser m\u00e1s costosas que otras, es un indicador m\u00e1s claro que dos atributos considerados de forma individual.\n",
        "\n",
        "Actualmente, la API de columnas de atributos solo admite atributos discretos para las combinaciones. Para combinar dos valores continuos, como `latitude` o `longitude`, podemos agruparlos.\n",
        "\n",
        "Si combinamos los atributos `latitude` y `longitude` (suponiendo, por ejemplo, que `longitude` se agrup\u00f3 en `2` agrupamientos, mientras que `latitude` tiene `3` agrupamientos), obtenemos seis atributos binarios combinados. Cada uno de esos atributos obtendr\u00e1 su propia ponderaci\u00f3n por separado cuando se entrene el modelo."
      ]
    },
    {
      "metadata": {
        "id": "-Rk0c1oTYaVH",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Tarea\u00a02: Entrena el modelo con combinaciones de atributos\n",
        "\n",
        "**Agrega una combinaci\u00f3n de atributos de `longitude` y `latitude` a tu modelo, entr\u00e9nalo y determina si mejoran los resultados.**\n",
        "\n",
        "Consulta la documentaci\u00f3n de la API de TensorFlow para obtener informaci\u00f3n sobre [`crossed_column()`](https://www.tensorflow.org/api_docs/python/tf/feature_column/crossed_column) a fin de crear la columna de atributos para tu combinaci\u00f3n. Usa un `hash_bucket_size` de `1000`."
      ]
    },
    {
      "metadata": {
        "id": "-eYiVEGeYhUi",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        },
        "cellView": "both"
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  # YOUR CODE HERE: Make a feature column for the long_x_lat feature cross\n",
        "  long_x_lat = \n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person,\n",
        "    long_x_lat])\n",
        "  \n",
        "  return feature_columns"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "xZuZMp3EShkM",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "0i7vGo9PTaZl",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ### Soluci\u00f3n\n",
        "\n",
        "Haz clic m\u00e1s abajo para conocer la soluci\u00f3n."
      ]
    },
    {
      "metadata": {
        "id": "3tAWu8qSTe2v",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  # YOUR CODE HERE: Make a feature column for the long_x_lat feature cross\n",
        "  long_x_lat = tf.feature_column.crossed_column(\n",
        "  set([bucketized_longitude, bucketized_latitude]), hash_bucket_size=1000) \n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person,\n",
        "    long_x_lat])\n",
        "  \n",
        "  return feature_columns"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "-_vvNYIyTtPC",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "ymlHJ-vrhLZw",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Desaf\u00edo opcional: Prueba m\u00e1s atributos sint\u00e9ticos\n",
        "\n",
        "Hasta el momento, hemos probado combinaciones de atributos y columnas agrupadas simples, pero hay muchas m\u00e1s combinaciones que posiblemente podr\u00edan mejorar los resultados. Por ejemplo, podr\u00edas combinar varias columnas. \u00bfQu\u00e9 ocurre si var\u00edas la cantidad de agrupamientos? \u00bfEn qu\u00e9 otras atributos sint\u00e9ticos puedes pensar? \u00bfMejoran el modelo?"
      ]
    }
  ]
}
